package calcs;

import gui.MyFrame;
import gui.data.D2.TriangleOutputGui;
import static util.Util.*;

import javax.swing.JTextArea;

public class TriangleCalculator extends Thread {

	JTextArea res;
	int guide;
	double abLength, bcLength, caLength, abHeight, bcHeight, caHeight,
			cabAngle, abcAngle, bcaAngle, circumference, area;
	MyFrame myFrame;

	public TriangleCalculator(int guide, double abLength, double bcLength,
			double caLength, double abHeight, double bcHeight, double caHeight,
			double cabAngle, double abcAngle, double bcaAngle,
			double circumference, double area, MyFrame myFrame) {
		this.guide = guide;
		this.abLength = abLength;
		this.bcLength = bcLength;
		this.caLength = caLength;
		this.abHeight = abHeight;
		this.bcHeight = bcHeight;
		this.caHeight = caHeight;
		this.cabAngle = cabAngle;
		this.abcAngle = abcAngle;
		this.bcaAngle = bcaAngle;
		this.circumference = circumference;
		this.area = area;
		this.myFrame = myFrame;
		start();
	}
	
	public TriangleCalculator(int guide, double abLength, double bcLength,
			double caLength, double abHeight, double bcHeight, double caHeight,
			double cabAngle, double abcAngle, double bcaAngle,
			double circumference, double area) {
		this.guide = guide;
		this.abLength = abLength;
		this.bcLength = bcLength;
		this.caLength = caLength;
		this.abHeight = abHeight;
		this.bcHeight = bcHeight;
		this.caHeight = caHeight;
		this.cabAngle = cabAngle;
		this.abcAngle = abcAngle;
		this.bcaAngle = bcaAngle;
		this.circumference = circumference;
		this.area = area;
	}
	
	public void run(){
		switch(guide){
		case 1:
			calculateEquilateral();
			break;
		case 2:
			calculateIsosceles();
			break;
		case 3:
			calculateScalene();
			break;
		case 4:
			calculateRight();
			break;
		}
		if(myFrame != null){
		myFrame.setDataPanel(new TriangleOutputGui(guide, abLength, bcLength,
				caLength, abHeight, bcHeight, caHeight,
				cabAngle, abcAngle, bcaAngle,
				circumference, area), "TRIANGLE_OUTPUT");
		}
	}
	private void calculateEquilateral() {
		boolean found;
		while(true){
			found = false;
			
			//if theres area and no sides, calculate sides
			if(area != 0 && countTriangleSides() == 0){
				abLength = Math.sqrt(area / (0.25 * Math.sqrt(3)));
				found = true;
			}
			
			//if there are heights, but no sides, calculate side
			if(countTriangleHeights() > 0){
				if(abHeight != 0){
					abLength = (2 * abHeight) / Math.sqrt(3);
				}else if(bcHeight != 0){
					abLength = (2 * bcHeight) / Math.sqrt(3);
				}else if(caHeight != 0){
					abLength = (2 * caHeight) / Math.sqrt(3);
				}
			}
			//if there is at least one angle missing, calculate triangle angles
			if(countTriangleAngles() < 3){
				abcAngle = 60;
				bcaAngle = 60;
				cabAngle = 60;
			}
			//all sides are equal
			if(countTriangleSides() < 3 && countTriangleSides() > 0){
				if(abLength != 0){
					bcLength = abLength;
					caLength = abLength;
				}else if(bcLength != 0){
					abLength = bcLength;
					caLength = bcLength;
				}else if(caLength != 0){
					abLength = caLength;
					bcLength = caLength;
				}
				found = true;
			}
			//all heights are the same
			if(countTriangleHeights() < 3 && countTriangleHeights() > 0){
				if(abHeight != 0){
					bcHeight = abHeight;
					caHeight = abHeight;
				}else if(bcHeight != 0){
					abHeight = bcHeight;
					caHeight = bcHeight;
				}else if(caHeight != 0){
					abHeight = caHeight;
					bcHeight = caHeight;
				}
				found = true;
			}
			//base ab and one of the sides yields abHeight
			if(abLength != 0 && bcLength != 0 && abHeight == 0){
				abHeight = Math.sqrt(Math.pow(bcLength, 2) - Math.pow(abLength / 2, 2));
				found = true;
			}
			//ab and abheight yield a side
			if(countTriangleSides() == 1 && abLength != 0 && abHeight != 0){
				bcLength = Math.sqrt(Math.pow(abLength / 2, 2) + Math.pow(abHeight, 2));
				found = true;
			}
			//area and one side yields side height, area and height yields side
			if(area != 0){
				if(abLength != 0 && abHeight == 0){
					abHeight = 2 * area / abLength;
					found = true;
				}
				if(abHeight != 0 && abLength == 0){
					abLength = 2 * area / abHeight;
					found = true;
				}
				if(bcLength != 0 && bcHeight == 0){
					bcHeight = 2 * area / bcLength;
					found = true;
				}
				if(bcHeight != 0 && bcLength == 0){
					bcLength = 2 * area / bcHeight;
					found = true;
				}
				if(caLength != 0 && caHeight == 0){
					caHeight = 2 * area / caLength;
					found = true;
				}
				if(caHeight != 0 && caLength == 0){
					caLength = 2 * area / caHeight;
					found = true;
				}
				
			}
			//circumference yields sides
			if(circumference != 0 && countTriangleSides() == 0){
				abLength = circumference / 3;
				bcLength = circumference / 3;
				caLength = circumference / 3;
				found = true;
			}
			
			
			//find area
			if(area == 0){
				findArea(found);
			}
			//side and one corner
			if(abLength != 0 && abcAngle != 0){
				if(abHeight == 0){
					abHeight = (abLength / 2) * Math.tan(Math.toRadians(abcAngle));
					found = true;
				}
				if(bcLength == 0){
					bcLength = (abLength / 2) / Math.sin(Math.toRadians(abcAngle));
					found = true;
				}	
			}
			
			//circumference
			if(countTriangleSides() == 3 && circumference == 0){
				findCircumference();
				found = true;
			}
			if(found){
				continue;
			}else{
				roundAnswers();
				break;
			}
			}
	}
	private int countTriangleHeights() {
		int count = 0;
		if(abHeight != 0) count ++;
		if(bcHeight != 0) count ++;
		if(caHeight != 0) count ++;
		return count;
	}

	//does not work with obtuse angles
	private void calculateIsosceles() {
		boolean found;
		while(true){
			found = false;
			
			//if there is at least one angle, calculate triangle angles
			if(countTriangleAngles() < 3 && countTriangleAngles() >= 1){
				if(abcAngle != 0){
					cabAngle = abcAngle;
					bcaAngle = 180 - (abcAngle * 2);
				}else if(cabAngle != 0){
					abcAngle = cabAngle;
					bcaAngle = 180 - (abcAngle * 2);
				}else if(bcaAngle != 0){
					cabAngle = (180 - bcaAngle) / 2;
					abcAngle = cabAngle;
				}
				found = true;
			}
			//one equal side is the same as the other
			if(bcLength != 0 && caLength == 0){
				caLength = bcLength;
				found = true;
			}else if(caLength != 0 && bcLength == 0){
				bcLength = caLength;
				found = true;
			}
			//heights of the equal sides are the same
			if(bcHeight != 0 && caHeight == 0){
				caHeight = bcHeight;
				found = true;
			}else if(caHeight != 0 && bcHeight == 0){
				bcHeight = caHeight;
				found = true;
			}
			//base ab and one of the sides yields abHeight
			if(abLength != 0 && bcLength != 0 && abHeight == 0){
				abHeight = Math.sqrt(Math.pow(bcLength, 2) - Math.pow(abLength / 2, 2));
				found = true;
			}
			//ab and abheight yield a side
			if(countTriangleSides() == 1 && abLength != 0 && abHeight != 0){
				bcLength = Math.sqrt(Math.pow(abLength / 2, 2) + Math.pow(abHeight, 2));
				found = true;
			}
			//area and one side yields side height, area and height yields side
			if(area != 0){
				if(abLength != 0 && abHeight == 0){
					abHeight = 2 * area / abLength;
					found = true;
				}
				if(abHeight != 0 && abLength == 0){
					abLength = 2 * area / abHeight;
					found = true;
				}
				if(bcLength != 0 && bcHeight == 0){
					bcHeight = 2 * area / bcLength;
					found = true;
				}
				if(bcHeight != 0 && bcLength == 0){
					bcLength = 2 * area / bcHeight;
					found = true;
				}
				if(caLength != 0 && caHeight == 0){
					caHeight = 2 * area / caLength;
					found = true;
				}
				if(caHeight != 0 && caLength == 0){
					caLength = 2 * area / caHeight;
					found = true;
				}
				
			}
			//circumference and side yield other sides
			if(circumference != 0 && countTriangleSides() == 1){
				if(abLength != 0){
					bcLength = (circumference - abLength) / 2;
					found = true;
				}else if(bcLength != 0){
					caLength = bcLength;
					abLength = circumference - (bcLength * 2);
					found = true;
				}
			}
			
			
			//find area
			if(area == 0){
				findArea(found);
			}
			//side and one corner
			if(abLength != 0 && abcAngle != 0){
				if(abHeight == 0){
					abHeight = (abLength / 2) * Math.tan(Math.toRadians(abcAngle));
					found = true;
				}
				if(bcLength == 0){
					bcLength = (abLength / 2) / Math.sin(Math.toRadians(abcAngle));
					found = true;
				}	
			}
			
			//no corners
			if(countTriangleAngles() == 0 && abLength != 0 && abHeight != 0){
				abcAngle = Math.toDegrees(Math.atan(abHeight / (abLength / 2)));
				found =  true;
			}
			
			//circumference
			if(countTriangleSides() == 3 && circumference == 0){
				findCircumference();
				found = true;
			}
			if(found){
				continue;
			}else{
				roundAnswers();
				break;
			}
			}
			
		}
	private void calculateScalene() {
		boolean found;
		while(true){
			found = false;
			
			//if there are two angles, calculate third
			if(countTriangleAngles() == 2){
				if(abcAngle == 0){
					abcAngle = 180 - (cabAngle + bcaAngle);
				}else if(cabAngle == 0){
					cabAngle = 180 - (bcaAngle + abcAngle);
				}else if(bcaAngle == 0){
					bcaAngle = 180 - (cabAngle + abcAngle);
				}
				found = true;
			}
			//if there are all angles and at least one side, calculate other sides
			if(countTriangleAngles() == 3 && countTriangleSides() > 0 && countTriangleSides() < 3){
				if(abLength != 0){
					if(bcLength == 0){
						bcLength = (abLength / Math.sin(Math.toRadians(bcaAngle))) * Math.sin(Math.toRadians(cabAngle));
					}
					if(caLength == 0){
						caLength = (abLength / Math.sin(Math.toRadians(bcaAngle))) * Math.sin(Math.toRadians(abcAngle));
					}
				}else if(bcLength != 0){
					if(abLength == 0){
						abLength = (bcLength / Math.sin(Math.toRadians(cabAngle))) * Math.sin(Math.toRadians(bcaAngle));
					}
					if(caLength == 0){
						caLength = (bcLength / Math.sin(Math.toRadians(cabAngle))) * Math.sin(Math.toRadians(abcAngle));
					}
				}else if(caLength != 0){
					if(bcLength == 0){
						bcLength = (caLength / Math.sin(Math.toRadians(abcAngle))) * Math.sin(Math.toRadians(cabAngle));
					}
					if(abLength == 0){
						abLength = (caLength / Math.sin(Math.toRadians(abcAngle))) * Math.sin(Math.toRadians(bcaAngle));
					}
				}
				found = true;
			}
			if(countTriangleSides() == 3 && circumference == 0){
				findCircumference();
				found = true;
			}
			//if there are two sides and circumference, calculate third side
			if(circumference != 0 && countTriangleSides() == 2){
				if(abLength == 0){
					abLength = circumference - (bcLength + caLength);
				}else if(bcLength == 0){
					bcLength = circumference - (abLength + caLength);
				}else if(caLength == 0){
					caLength = circumference - (abLength + bcLength);
				}
			found = true;
			}
			//area and one side yields side height, area and height yields side
			if(area != 0){
				if(abLength != 0 && abHeight == 0){
					abHeight = 2 * area / abLength;
					found = true;
				}
				if(abHeight != 0 && abLength == 0){
					abLength = 2 * area / abHeight;
					found = true;
				}
				if(bcLength != 0 && bcHeight == 0){
					bcHeight = 2 * area / bcLength;
					found = true;
				}
				if(bcHeight != 0 && bcLength == 0){
					bcLength = 2 * area / bcHeight;
					found = true;
				}
				if(caLength != 0 && caHeight == 0){
					caHeight = 2 * area / caLength;
					found = true;
				}
				if(caHeight != 0 && caLength == 0){
					caLength = 2 * area / caHeight;
					found = true;
				}
				
			}
			
			//one angle and two sides (one of which is opposite of the angle) yield second angle
			if(countTriangleSides() > 1 && countTriangleAngles() == 1){
				if(abcAngle != 0 && caLength != 0){
					if(bcLength != 0){
						cabAngle = Math.toDegrees(Math.asin((bcLength * Math.sin(Math.toRadians(abcAngle)))/caLength));
						found = true;
					}else if(abLength != 0){
						bcaAngle = Math.toDegrees(Math.asin((abLength * Math.sin(Math.toRadians(abcAngle)))/caLength));
						found = true;
					}
				}else if(bcaAngle != 0 && abLength != 0){
					if(bcLength != 0){
						cabAngle = Math.toDegrees(Math.asin((bcLength * Math.sin(Math.toRadians(bcaAngle)))/abLength));
						found = true;
					}else if(caLength != 0){
						abcAngle = Math.toDegrees(Math.asin((caLength * Math.sin(Math.toRadians(bcaAngle)))/abLength));
						found = true;
					}
				}else if(cabAngle != 0 && bcLength != 0){
					if(caLength != 0){
						abcAngle = Math.toDegrees(Math.asin((caLength * Math.sin(Math.toRadians(cabAngle)))/bcLength));
						found = true;
					}else if(abLength != 0){
						bcaAngle = Math.toDegrees(Math.asin((abLength * Math.sin(Math.toRadians(cabAngle)))/bcLength));
						found = true;
					}
				}
			}
			
			//three sides yield a corner
			if(countTriangleSides() == 3 && countTriangleAngles() == 0){
				cabAngle = Math.toDegrees(Math.acos((Math.pow(caLength, 2) + Math.pow(abLength, 2) - Math.pow(bcLength, 2)) / (2 * caLength * abLength)));	
				found = true;
			}
			
			//heights from side and corner
			if(abHeight == 0){
				if(bcLength != 0 && abcAngle != 0){
					abHeight = bcLength * Math.sin(Math.toRadians(abcAngle));
					found = true;
				}else if(caLength != 0 && cabAngle != 0){
					abHeight = caLength * Math.sin(Math.toRadians(cabAngle));
					found = true;
				}
			}
			if(bcHeight == 0){
				if(caLength != 0 && bcaAngle != 0){
					bcHeight = caLength * Math.sin(Math.toRadians(bcaAngle));
					found = true;
				}else if(abLength != 0 && abcAngle != 0){
					bcHeight = abLength * Math.sin(Math.toRadians(abcAngle));
					found = true;
				}
			}
			if(caHeight == 0){
				if(abLength != 0 && cabAngle != 0){
					caHeight = abLength * Math.sin(Math.toRadians(cabAngle));
					found = true;
				}else if(bcLength != 0 && bcaAngle != 0){
					caHeight = bcLength * Math.sin(Math.toRadians(bcaAngle));
					found = true;
				}
			}
			
			//corner from height and side
			if(abcAngle == 0){
				if(abHeight != 0 && bcLength != 0){
					abcAngle = Math.toDegrees(Math.asin(abHeight / bcLength));
					found = true;
				}else if(bcHeight != 0 && abLength != 0){
					abcAngle = Math.toDegrees(Math.asin(bcHeight / abLength));
					found = true;
				}
			}
			if(bcaAngle == 0){
				if(bcHeight != 0 && caLength != 0){
					bcaAngle = Math.toDegrees(Math.asin(bcHeight / caLength));
					found = true;
				}else if(caHeight != 0 && bcLength != 0){
					bcaAngle = Math.toDegrees(Math.asin(caHeight / bcLength));
					found = true;
				}
			}
			if(cabAngle == 0){
				if(abHeight != 0 && caLength != 0){
					cabAngle = Math.toDegrees(Math.asin(abHeight / caLength));
					found = true;
				}else if(caHeight != 0 && abLength != 0){
					cabAngle = Math.toDegrees(Math.asin(caHeight / abLength));
					found = true;
				}
			}
			//side from height and corner
			if(abLength == 0){
				if(bcHeight != 0 && abcAngle != 0){
					abLength = bcHeight / Math.sin(Math.toRadians(abcAngle));
					found = true;
				}else if(caHeight != 0 && cabAngle != 0){
					abLength = caHeight / Math.sin(Math.toRadians(cabAngle));
					found = true;
				}
			}
			if(bcLength == 0){
				if(abHeight != 0 && abcAngle != 0){
					bcLength = abHeight / Math.sin(Math.toRadians(abcAngle));
					found = true;
				}else if(caHeight != 0 && bcaAngle != 0){
					bcLength = caHeight / Math.sin(Math.toRadians(bcaAngle));
					found = true;
				}
			}
			if(caLength == 0){
				if(abHeight != 0 && cabAngle != 0){
					caLength = abHeight / Math.sin(Math.toRadians(cabAngle));
					found = true;
				}else if(bcHeight != 0 && bcaAngle != 0){
					caLength = bcHeight / Math.sin(Math.toRadians(bcaAngle));
					found = true;
				}
			}
			//find area
			if(area == 0){
				findArea(found);
			}
			//circumference
			if(countTriangleSides() == 3 && circumference == 0){
				findCircumference();
				found = true;
			}
			if(found){
				continue;
			}else{
				break;
			}
			}
		roundAnswers();
	}
	private void calculateRight() {
		boolean found;
		bcaAngle = 90;
		while(true){
			found = false;
			
			//triangle has two sides
			if(countTriangleSides() == 2){
				if(bcLength == 0){
					bcLength = doPythagoras(bcLength, caLength, abLength);
				}else if(caLength == 0){
					caLength = doPythagoras(bcLength, caLength, abLength);
				}else if(abLength == 0){
					abLength = doPythagoras(bcLength, caLength, abLength);
				}
				found = true;
			}
			//sides bc and ca are also heights
			if(bcLength != 0 && caHeight == 0){
				caHeight = bcLength;
				found = true;
			}
			if(caLength != 0 && bcHeight == 0){
				bcHeight = caLength;
				found = true;
			}
			//triangle has two angles
			if(countTriangleAngles() == 2){
				if(cabAngle == 0){
					cabAngle = (90-abcAngle);
				}else if(abcAngle == 0){
					abcAngle = (90-cabAngle);
				}
				found = true;
			}
			//sides from one side and angles
			if(countTriangleSides() == 1 && cabAngle != 0){
				if(bcLength != 0){
					caLength = bcLength / Math.tan(Math.toRadians(cabAngle));
				}else if(caLength != 0){
					bcLength = caLength * Math.tan(Math.toRadians(cabAngle));
				}else if(abLength != 0){
					bcLength = abLength * Math.sin(Math.toRadians(cabAngle));
				}
				found = true;
			}
			
			if(countTriangleSides() == 3 && circumference == 0){
				findCircumference();
				found = true;
			}
			//there is at least one side and its height
			if(area == 0){
				findArea(found);
			}
			//area and abHeight yield a side
			if(area != 0 && abHeight != 0 && abLength == 0){
				abLength = 2 * area / abHeight;
				found = true;
			}
			//area and abLength yield abHeight
			if(area != 0 && abLength != 0 && abHeight == 0){
				abHeight = 2 * area / abLength;
				found = true;
			}
			//area and side bc or ca
			if(countTriangleSides() == 1 && abLength == 0 && area != 0){
				if(bcLength != 0){
					caLength = 2 * area / bcLength;
				}else if(caLength != 0){
					bcLength = 2 * area / caLength;
				}
				found = true;
			}
			//abHeight and cabAngle or abcAngle
			if(abHeight != 0 && (cabAngle != 0 || abcAngle != 0) && (caLength == 0 || bcLength == 0)){
				if(cabAngle !=0 && caLength == 0){
					caLength = abHeight / Math.sin(Math.toRadians(cabAngle));
				}else if(abcAngle !=0 && bcLength == 0){
					bcLength = abHeight / Math.sin(Math.toRadians(abcAngle));
				}
				found = true;
			}
			//abHeight and side ca or bc
			if(abHeight != 0 && (caLength != 0 || bcLength != 0) && countTriangleAngles() == 1){
				if(caLength != 0){
					cabAngle = Math.toDegrees(Math.asin(abHeight/caLength));
				}else if(bcLength !=0){
					abcAngle = Math.toDegrees(Math.asin(abHeight/bcLength));
				}
				found = true;
			}
			if(found){
				continue;
			}else{
				break;
			}
		}
		roundAnswers();
	}
	private int countTriangleAngles() {
		int count = 0;
		if(bcaAngle != 0){
			count ++;
		}
		if(cabAngle != 0){
			count ++;
		}
		if(abcAngle != 0){
			count ++;
		}
		return count;
	}

	private int countTriangleSides() {
		int count = 0;
		if(abLength != 0){
			count ++;
		}
		if(bcLength != 0){
			count ++;
		}
		if(caLength != 0){
			count ++;
		}
		return count;
	}

	private double doPythagoras(double a, double b,
			double c) {
		if(a != 0 && b != 0){
			return Math.sqrt(Math.pow(a, 2)+Math.pow(b, 2));
		}
		else if(a != 0 && c != 0){
			return Math.sqrt(Math.pow(c, 2)-Math.pow(a, 2));
		}
		else if(b != 0 && c != 0){
			return Math.sqrt(Math.pow(c, 2)-Math.pow(b, 2));
		}else{
			return 10;
		}
		
	}
	
	private void findArea(boolean found){
		if(abLength != 0 && abHeight != 0){
			area = abLength * abHeight / 2;
			found = true;
		}else if(bcLength != 0 && bcHeight != 0){
			area = bcLength * bcHeight / 2;
			found = true;
		}else if(caLength != 0 && caHeight != 0){
			area = caLength * caHeight / 2;
			found = true;
		}
	}
	
	private void findCircumference(){
		circumference = abLength + bcLength + caLength;
	}
	
	private void roundAnswers(){
		abLength = roundToDecimals(abLength, 2);
		bcLength = roundToDecimals(bcLength, 2);
		caLength = roundToDecimals(caLength, 2);
		abHeight = roundToDecimals(abHeight, 2);
		bcHeight = roundToDecimals(bcHeight, 2);
		caHeight = roundToDecimals(caHeight, 2);
		cabAngle = roundToDecimals(cabAngle, 2);
		abcAngle = roundToDecimals(abcAngle, 2);
		bcaAngle = roundToDecimals(bcaAngle, 2);
		circumference = roundToDecimals(circumference, 2);
		area = roundToDecimals(area, 2);
	}
}
